
#include "cutting_rect.hpp"

#define TTDS_MIN(a, b) ((a) < (b) ? (a) : (b))
#define TTDS_MAX(a, b) ((a) < (b) ? (b) : (a))

int ttds_rect_intersection(const ttds_rect *a, const ttds_rect *b, ttds_rect *r)
{
    int x1 = a->x;
    int y1 = a->y;
    int x2 = x1 + a->width - 1;
    int y2 = y1 + a->height - 1;
    int x3 = b->x;
    int y3 = b->y;
    int x4 = x3 + b->width - 1;
    int y4 = y3 + b->height - 1;

    int x = TTDS_MAX(x1, x3);
    int y = TTDS_MAX(y1, y3);
    int xx = TTDS_MIN(x2, x4);
    int yy = TTDS_MIN(y2, y4);

    int ok = (x <= xx && y <= yy);
    if (r)
    {
        if (ok)
        {
            r->x = x;
            r->y = y;
            r->width = xx - x + 1;
            r->height = yy - y + 1;
        }
        else
        {
            r->x = r->y = r->width = r->height = 0;
        }
    }
    return ok;
}
static int __ttds_rect_apart(const ttds_rect *a, const ttds_rect *b, ttds_rect ra[4], ttds_rect rb[4])
{
    ttds_rect r;
    int x1 = a->x;
    int y1 = a->y;
    int x2 = x1 + a->width - 1;
    int y2 = y1 + a->height - 1;
    int x3 = b->x;
    int y3 = b->y;
    int x4 = x3 + b->width - 1;
    int y4 = y3 + b->height - 1;

#define __cmp(a, b, c, d) (a <= b && b <= c && c <= d)
#define RECT_X(r, a, b, c, d)     \
    do                            \
    {                             \
        r.x = a;                  \
        r.y = b;                  \
        r.width = (c) - (a) + 1;  \
        r.height = (d) - (b) + 1; \
    } while (0)

    if (__cmp(x1, x3, x4, x2) && __cmp(y1, y3, y2, y4))
    {
        /*
                             _____(x4, y4)
                            |     |
                    |-------|-----#-------# (x2, y2)
                    |       |     |       |
                    |       #-----|       |
                    |       (x3,y3)       |
                    |                     |
                    |                     |
                    |                     |
                    #----------------------
                    (x1,y1)
        */
        /* A: [x1,y1]->(x3,y2]   [x3,y1]->[x4,y3)  (x4,y1]->[x2,y2] */
        RECT_X(ra[0], x1, y1, x3 - 1, y2);
        RECT_X(ra[1], x3, y1, x4, y3 - 1);
        RECT_X(ra[2], x4 + 1, y1, x2, y2);

        /* B: [x3,y2)->[x4,y4] */
        RECT_X(rb[0], x3, y2 + 1, x4, y4);
        return 1;
    }
    else if (__cmp(x1, x3, x2, x4) && __cmp(y1, y3, y4, y2))
    {
        /*
                    condition:
                        x1 <= x3 <= x2 <= x4
                        y1 <= y3 <= y4 <= y2
                    rects:
                        A: [x1,y1]->[x2,y3)  [x1,y3]->(x3,y4] [x1,y4)->[x2,y2]
                        B: (x2,y3]->[x4,y4]


                    |---------------------# (x2, y2)
                    |                     |
                    |                 ____#___#(x4,y4)
                    |                 |   |   |
                    |                 #___|___|
                    |            (x3,y3)  |
                    |                     |
                    #----------------------
                    (x1,y1)
        */
        RECT_X(ra[0], x1, y1, x2, y3 - 1);
        RECT_X(ra[1], x1, y3, x3 - 1, y4);
        RECT_X(ra[2], x1, y4 + 1, x2, y2);

        RECT_X(rb[0], x2 + 1, y3, x4, y4);
        return 1;
    }
    else if (__cmp(x1, x3, x4, x2) && __cmp(y3, y1, y4, y2))
    {
        /*
            condition:
                x1 <= x3 <= x4 <= x2
                y3 <= y1 <= y4 <= y2
            rects:
                A: [x1,y1]->(x3,y4] (x4,y1]->[x2,y4] [x1,y4)->[x2,y2]
                B: [x3,y3]->[x4,y1)


            |---------------------# (x2, y2)
            |                     |
            |                     |
            |                     |
            |            (x4,y4)  |
            |        _______      |
            |        |     |      |
            #--------|-----|-------
            (x1,y1)  |     |
                     |     |
                     -------
                    (x3,y3)
        */
        RECT_X(ra[0], x1, y1, x3 - 1, y4);
        RECT_X(ra[1], x4 + 1, y1, x2, y4);
        RECT_X(ra[2], x1, y4 + 1, x2, y2);

        RECT_X(rb[0], x3, y3, x4, y1 - 1);
        return 1;
    }
    else if (__cmp(x3, x1, x4, x2) && __cmp(y1, y3, y4, y2))
    {
        /*
            condition:
                x3 <= x1 <= x4 <= x2
                y1 <= y3 <= y4 <= y2
            rects:
                A: [x1,y1]->[x4,y3) [x1,y4)->[x4,y2] (x4,y1]->[x2,y2]
                B: [x3,y3]->(x1,y4]

            |---------------------# (x2, y2)
        ____|___#(x4,y4)          |
       |    |   |                 |
       |    |   |                 |
       |    |   |                 |
       #----|----                 |
 (x3,y3)    |                     |
            #----------------------
            (x1,y1)
        */
        RECT_X(ra[0], x1, y1, x4, y3 - 1);
        RECT_X(ra[1], x1, y4 + 1, x4, y2);
        RECT_X(ra[2], x4 + 1, y1, x2, y2);

        RECT_X(rb[0], x3, y3, x1 - 1, y4);
        return 1;
    }
    else if (__cmp(x3, x1, x4, x2) && __cmp(y1, y3, y2, y4))
    {
        /*
            condition:
                x3 <= x1 <= x4 <= x2
                y1 <= y3 <= y2 <= y4
            rects:
                A: [x1,y1]->[x2,y3)  (x4,y3]->[x2,y2]
                B: [x3,y3]->(x1,y4]  [x1,y2)->[x4,y4]

                        |-----------|(x4,y4)
                        |           |
                        |      |----|----------------# (x2, y2)
                        |      |    |                |
                        |      |    |                |
                 (x3,y3)|------|----|                |
                               |                     |
                               |                     |
                               |                     |
                               #----------------------
                               (x1,y1)
        */
        RECT_X(ra[0], x1  , y1  , x2  , y3-1 );
        RECT_X(ra[1], x4+1, y3  , x2  , y2   );
        
        RECT_X(rb[0], x3  , y3  , x1-1, y4   );
        RECT_X(rb[1], x1  , y2+1, x4  , y4   );
        return 1;
    } else if (__cmp(x1, x3, x2, x4) && __cmp(y1, y3, y2, y4)){
        /*
            condition:
                x1 <= x3 <= x2 <= x4
                y1 <= y3 <= y2 <= y4
            rects:
                A: [x1,y1]->(x3,y2] [x3,y1]->[x2,y3)
                B: (x2,y3]->[x4,y4] [x3,y2)->[x2,y4]

                                                 |----------------|(x4,y4)
                                                 |                |
                               |-----------------|---# (x2, y2)   |
                               |                 |   |            |
                               |                 |   |            |
                               |                 |---|-------------
                               |            (x3,y3)  |
                               |                     |
                               |                     |
                               #----------------------
                               (x1,y1)
        */
        ;
        RECT_X(ra[0], x1  , y1  , x3-1, y2   );
        RECT_X(ra[1], x3  , y1  , x2  , y3-1 );

        RECT_X(rb[0], x2+1, y3  , x4  , y4   );
        RECT_X(rb[1], x3  , y2+1, x2  , y4   );
        return 1;
    } else if (__cmp(x1, x3, x4, x2) && __cmp(y1, y3, y4, y2)){
        /*
            condition:
                x1 <= x3 <= x4 <= x2
                y1 <= y3 <= y4 <= y2
            rects:
                A: [x1,y1]->[x2,y3)  [x1,y4)->[x2,y2]  [x1,y3]->(x3,y4] (x4,y3]->[x2,y4]
                B: -------EMPTY---------



                               |---------------------# (x2, y2)
                               |  ----------------|  |
                               |  |        (x4,y4)|  |
                               |  |               |  |
                               |  |               |  |
                               |  |---------------|  |
                               | (x3,y3)             |
                               #----------------------
                               (x1,y1)
        */
        ;
        RECT_X(ra[0], x1  , y1  , x2  , y3-1 );
        RECT_X(ra[1], x1  , y4+1, x2  , y2   );
        RECT_X(ra[2], x1  , y3  , x3-1, y4   );
        RECT_X(ra[3], x4+1, y3  , x2  , y4   );
        return 1;
    } else if (__cmp(x1, x3, x4, x2) && __cmp(y3, y1, y2, y4)){
        /*
            condition:
                x1 <= x3 <= x4 <= x2
                y3 <= y1 <= y2 <= y4
            rects:
                A: [x1,y1]->(x3,y2]  (x4,y1]->[x2,y2]
                B: [x3,y3]->[x4,y1)  [x3,y2)->[x4,y4]

                                      |-------|(x4,y4)
                                      |       |
                               |------|-------|-------# (x2, y2)
                               |      |       |       |
                               |      |       |       |
                               |      |       |       |
                               |      |       |       |
                               |      |       |       |
                               |      |       |       |
                               #------|-------|--------
                               (x1,y1)|       |
                                      |       |
                                      |-------|
                                    (x3,y3)
        */
        RECT_X(ra[0], x1  , y1  , x3-1, y2   );
        RECT_X(ra[1], x4+1, y1  , x2  , y2   );

        RECT_X(rb[0], x3  , y3  , x4  , y1-1 );
        RECT_X(rb[1], x3  , y2+1, x4  , y4   );
        return 1;
    }

#undef RECT_X
#undef __cmp
    return 0;
}
static void __rect_rmove_zero( ttds_rect *ra, int n)
{
    int i;

    for (i=0; i<n; i++ ){
        if (ra[i].width<=0 || ra[i].height<=0){
            ra[i].width = 0;
            ra[i].height = 0;
        }
    }
    for (i=0; i<n; i++ ){
        int k = i;
        while (k < n && ra[k].width == 0){
            k++;
        }
        if ( k != i && k<n && ra[k].width > 0){
            ra[i] = ra[k];
            ra[k].x = 0;
            ra[k].y = 0;
            ra[k].width = 0;
            ra[k].height = 0;
        }
    }
}

int ttds_rect_cut(const ttds_rect *a, const ttds_rect *b, ttds_rect ra[4], ttds_rect rb[4])
{
    int ok = __ttds_rect_apart(a, b, ra, rb);
    if (!ok){
        ok = __ttds_rect_apart(b, a, rb, ra);
    }
    __rect_rmove_zero( ra, 4 );
    __rect_rmove_zero( rb, 4 );
    return ok;
}
